#include "PreRenderContext.h"

#include "Render/Texture/RenderTexture2D.h"
#include "Render/Texture/RenderTextureCube.h"
#include "Render/Mesh/RenderMesh.h"
#include "Render/Material/RenderMaterial.h"
#include "Render/Material/RenderShader.h"

#include "Render/PreRender/PreRenderTexture2D.h"
#include "Render/PreRender/PreRenderTextureCube.h"
#include "Render/PreRender/PreRenderMesh.h"
#include "Render/PreRender/PreRenderShader.h"
#include "Render/PreRender/PreRenderMaterial.h"
#include "Render/PreRender/PreRenderResult.h"


std::shared_ptr<CPreRenderTexture2D> SPreRenderContext::PreRender(std::shared_ptr<CRenderTexture2D> Texture)
{
    auto WriteInTexture = Texture->GetWriteInTexture();
    if(Texture2DMap.Contains(WriteInTexture))
    {
        return Texture2DMap[WriteInTexture];
    }
    else
    {
        auto PreRenderTexture = std::make_shared<CPreRenderTexture2D>();
        PreRenderTexture->Pixels = WriteInTexture;
        Texture2DMap.Add(WriteInTexture, PreRenderTexture);
        Result->Texture2Ds.Add(PreRenderTexture);
        return PreRenderTexture;
    }
}

std::shared_ptr<CPreRenderTextureCube> SPreRenderContext::PreRender(std::shared_ptr<CRenderTextureCube> Texture)
{
    auto WriteInTexture = Texture->GetWriteInTexture();
    if(TextureCubeMap.Contains(WriteInTexture))
    {
        return TextureCubeMap[WriteInTexture];
    }
    else
    {
        auto PreRenderTexture = std::make_shared<CPreRenderTextureCube>();
        PreRenderTexture->WriteInData = WriteInTexture;
        TextureCubeMap.Add(WriteInTexture, PreRenderTexture);
        Result->TextureCubes.Add(PreRenderTexture);
        return PreRenderTexture;
    }
}

std::shared_ptr<CPreRenderMesh>  SPreRenderContext::PreRender(std::shared_ptr<CRenderMesh> Mesh)
{
    auto WriteInMesh = Mesh->GetWriteInMesh();
    if(MeshMap.Contains(WriteInMesh))
    {
        return MeshMap[WriteInMesh];
    }
    else
    {
        auto PreRenderMesh = std::make_shared<CPreRenderMesh>();
        PreRenderMesh->WriteInData = WriteInMesh;
        MeshMap.Add(WriteInMesh, PreRenderMesh);
        Result->Meshes.Add(PreRenderMesh);
        return PreRenderMesh;
    }

}

std::shared_ptr<CPreRenderShader> SPreRenderContext::PreRender(std::shared_ptr<CRenderShader> Shader)
{
    auto WriteInShader = Shader->GetWriteInShader();
    if(ShaderMap.Contains(WriteInShader))
    {
        return ShaderMap[WriteInShader];
    }
    else
    {
        auto PreRenderShader = std::make_shared<CPreRenderShader>();
        PreRenderShader->WriteInData = WriteInShader;
        ShaderMap.Add(WriteInShader, PreRenderShader);
        Result->Shaders.Add(PreRenderShader);
        return PreRenderShader;
    }
}

std::shared_ptr<CPreRenderMaterial> SPreRenderContext::PreRender(std::shared_ptr<CRenderMaterial> Material)
{
    if(MaterialMap.Contains(Material))
    {
        return MaterialMap[Material];
    }

    Material->Mutex.lock();
    auto PreRenderMaterial = std::make_shared<CPreRenderMaterial>();
    PreRenderMaterial->Pipeline.bDoubleSided = Material->bDoubleSided;
    PreRenderMaterial->Pipeline.Type = Material->Type;
    PreRenderMaterial->Pipeline.VertexShader = PreRender(Material->VertexShader);
    PreRenderMaterial->Pipeline.FragmentShader = PreRender(Material->FragmentShader);

    PreRenderMaterial->Parameters.Parameters = Material->Parameters;
    PreRenderMaterial->Parameters.Texture2Ds.Reserve(Material->Texture2Ds.Num());
    for(auto& Texture2D: Material->Texture2Ds)
    {
        PreRenderMaterial->Parameters.Texture2Ds.Add(PreRender(Texture2D));
    }
    PreRenderMaterial->Parameters.TextureCubes.Reserve(Material->TextureCubes.Num());
    for(auto& TextureCube: Material->TextureCubes)
    {
        PreRenderMaterial->Parameters.TextureCubes.Add(PreRender(TextureCube));
    }

    Material->Mutex.unlock();

    MaterialMap.Add(Material, PreRenderMaterial);
    Result->Materials.Add(PreRenderMaterial);
    return PreRenderMaterial;

}